//
//  VPaiCtrlWebSocket.swift
//  VPai
//
//  Created by devel on 16/9/19.
//  Copyright © 2016年 viatech. All rights reserved.
//

import Foundation
import SwiftWebSocket
import SwiftyJSON

class VPaiCtrlWebSocket: NSObject {
    
    //单例模式
    class var shareInstance : VPaiCtrlWebSocket{
        struct Static {
            static var instance: VPaiCtrlWebSocket?
            static var token: dispatch_once_t = 0
        }
        
        dispatch_once(&Static.token){
            Static.instance = VPaiCtrlWebSocket()
        }
        
        return Static.instance!
    }
    
    func openWebSocket(url: String){
        Log.info(VPaiCtrlWebSocket.TAG, msg: "openWebSocket:url = \(url)")
        websocketUrl = url
        if(_ws != nil){
            closeWebSocket()
        }
        
        let ws = WebSocket(url)
        ws.event.open = onWSOpen
        ws.event.end = onWSEnd
        ws.event.message = onWSMessage
        _ws = ws;
    }
    
    func closeWebSocket(){
        _ws?.close()
        _ws = nil
        connected = false
        //clear callback map also
        for t in _replyTimer {
            t.1.invalidate()
        }
        for k in _replyMap {
            let args = CallbackArgs()
            k.1(args: args)
        }
        _replyMap.removeAll()
        _replyTimer.removeAll()
    }
    
    func getDeviceInfo(cb: CallbackFunc){
        let json = SwiftyJSON.JSON([
            "action" : ACTION_GET_DEVICE_INFO,
            "random" : getRandomNumberString(14)
            ])
        sendStringData(json, cb: cb)
    }
    
    func getFileList(cb: CallbackFunc) {
        let json = SwiftyJSON.JSON([
            "action" : ACTION_FILE_LIST
            ])
        sendStringData(json, cb: cb)
    }
    
    func deleteFile(list: [MediaFile], cb: CallbackFunc){
        var array = ""
        for file in list {
            if !array.isEmpty{
                array = array + ","
            }
            array = array + "{\"filename\":\"\(file.nameWithExt)\"}"
        }
        let jsonString = "{\"action\":\"\(ACTION_DELETE_FILE)\", \"list\":[\(array)]}"
        sendStringData(SwiftyJSON.JSON.parse(jsonString), cb: cb)
    }
    
    func getNetConfig(cb: CallbackFunc) {
        let json = SwiftyJSON.JSON([
            "action" : ACTION_GET_NET_CONFIG
            ])
        sendStringData(json, cb: cb)
    }
    
    func setNetConfig(config: NetConfig, cb: CallbackFunc) {
        let json = SwiftyJSON.JSON([
            "action" : ACTION_SET_NET_CONFIG,
            "wifimode" : config.wifimode,
            "softapssid" : config.softapssid,
            "softappasswd" : config.softappasswd,
            "stassid" : config.stassid,
            "stapasswd" : config.stapasswd
            ])
        sendStringData(json, cb: cb)
    }
    
    func setDateTime(dateAndTime: DateTimeInfo, cb: CallbackFunc) {
        let json = SwiftyJSON.JSON([
            "action" : ACTION_SYNCTIME,
            "date" : dateAndTime.date,
            "time" : dateAndTime.time,
            "timezone" : dateAndTime.tiemzone
            ])
        sendStringData(json, cb: cb)
    }
    
    func setLanguage(language: LanguageType, cb: CallbackFunc){
        let json = SwiftyJSON.JSON([
            "action" : ACTION_SET_LANGUAGE,
            "language" : language.rawValue
            ])
        sendStringData(json, cb: cb)
    }
    
    func setCurrentSystemLanguage(cb: CallbackFunc){
        let languages = NSLocale.preferredLanguages()
        let currentLanguage = languages[0]
        var language = LanguageType.EN
        if currentLanguage.hasPrefix("zh-Hans") {
            language = LanguageType.SC
        }else if currentLanguage.hasPrefix("zh-Hant") {
            language = LanguageType.TC
        }else if currentLanguage.containsString("zh"){
            language = LanguageType.ZH
        }
        setLanguage(language, cb: cb)
    }
    
    func setDeviceID(id: Int, cb: CallbackFunc) {
        let json = SwiftyJSON.JSON([
            "action" : ACTION_SET_DEVICEID,
            "id" : id
            ])
        sendStringData(json, cb: cb)
    }
    
    func updateDevice(size: Int, cb: CallbackFunc) {
        let json = SwiftyJSON.JSON([
            "action" : ACTION_UPDATE_DEVICE,
            "filesize" : "\(size)"
            ])
        sendStringData(json, cb: cb)
    }
    
    func restartDevice() {
        let json = SwiftyJSON.JSON([
            "action" : ACTION_RESTART_DEVICE
            ])
        sendStringData(json)
    }
    
    func changeHotspotPassword(password: String, cb: CallbackFunc) {
        let json = SwiftyJSON.JSON([
            "action" : ACTION_SET_WIFI_APINFO,
            "password" : password
            ])
        sendStringData(json, cb: cb)
    }
    
    func enableInternet(enable: Bool, cb: CallbackFunc) {
        let json = SwiftyJSON.JSON([
            "action" : ACTION_ENABLE_INTERNET,
            "status" : enable ? 1 : 0
            ])
        sendStringData(json, cb: cb)
    }
    
    func getWifilist(cb: CallbackFunc) {
        let json = SwiftyJSON.JSON([
            "action" : ACTION_GET_WIFILIST
            ])
        sendStringData(json, cb: cb)
    }
    
    func setWifiStationInfo(info: WifiInfo, password: String, cb: CallbackFunc) {
        let json = SwiftyJSON.JSON([
            "action" : ACTION_SET_WIFI_STATIONINFO,
            "ssid" : info.ssid,
            "security" : info.security,
            "password" : password
            ])
        sendStringData(json, cb: cb)
    }
    
    //and set onXXX function. You should save it and call unregisterEvent later.
    func registerEvent() -> VPaiCtrlWebSocketEvents{
        let e = VPaiCtrlWebSocketEvents()
        e.id = _nextEventId
        _nextEventId += 1
        _listenEvents.append(e)
        return e
    }
    
    //unregister it. e should be the return value of registerEvent()
    func unregisterEvent(e : VPaiCtrlWebSocketEvents){
        var idx = 0
        for d in _listenEvents {
            if d.id == e.id {
                _listenEvents.removeAtIndex(idx)
                return
            }
            idx += 1
        }
    }
    
    func sendStringData(json: SwiftyJSON.JSON, cb:CallbackFunc?=nil){
        if let ws = _ws where connected {
            _cookie += 1
            var newJson = json
            newJson["cookie"] = SwiftyJSON.JSON(_cookie)
            if cb != nil {
                _replyTimer[_cookie] = NSTimer.scheduledTimerWithTimeInterval(timeout, target: self,
                                            selector: #selector(VPaiCtrlWebSocket.sentTimeout(_:)),
                                            userInfo: _cookie, repeats: false)
                _replyMap[_cookie] = cb
            }
           // Log.info(VPaiCtrlWebSocket.TAG, msg: "sendStringData:\(newJson.rawString()!)")
            ws.send(text: newJson.rawString()!)
        }else {
           // Log.info(VPaiCtrlWebSocket.TAG, msg: "sendStringData: no connection")
            dispatch_async(dispatch_get_main_queue(), {
                let args = CallbackArgs()
                cb?(args: args)
            })
        }
        
    }
    
    func sendData(data: NSData) {
        if let ws = _ws {
            ws.send(data: data)
        }else {
          //  Log.info(VPaiCtrlWebSocket.TAG, msg: "sendData: no connection")
        }
    }
    
    func sentTimeout(t: NSTimer) {
        if let cookie = t.userInfo as? Int {
            _replyTimer[cookie]?.invalidate()
            _replyTimer[cookie] = nil
            if let cb = _replyMap[cookie] {
                _replyMap[cookie] = nil
                let args = CallbackArgs()
                dispatch_async(dispatch_get_main_queue(), {
                    cb(args: args)
                })
            }
        }
    }
    
    private static let TAG = "VPaiCtrlWebSocket"
    private let ACTION_GET_DEVICE_INFO = "getdeviceinfo"
    private let ACTION_FILE_LIST = "filelist"
    private let ACTION_DELETE_FILE = "deletefiles"
    private let ACTION_GET_NET_CONFIG = "getnetconfig"
    private let ACTION_SET_NET_CONFIG = "setnetconfig"
    private let ACTION_SYNCTIME = "synctime"
    private let ACTION_SET_LANGUAGE = "setlanguage"
    private let ACTION_SET_DEVICEID = "setdeviceid"
    private let ACTION_UPDATE_DEVICE = "updatedevice"
    private let ACTION_RESTART_DEVICE = "restartdevice"
    private let ACTION_UPDATE_DEVICE_RESPONSE = "updatedevice_response"
    private let ACTION_SET_WIFI_APINFO = "setwifiapinfo"
    private let ACTION_ENABLE_INTERNET = "enableinternet"
    private let ACTION_GET_WIFILIST = "getwifilist"
    private let ACTION_SET_WIFI_STATIONINFO = "setwifistationinfo"
    private let ACTION_NOTIFY_WIFI_STATUS = "notifywifistatus"
    
    private var _ws : WebSocket?
    private var connected = false
    private var _nextEventId:Int = 1
    private var _listenEvents : [VPaiCtrlWebSocketEvents] = []
    private var websocketUrl = ""
    //Key is cookie(Int type), vaule is a callback clousure
    private var _replyMap: Dictionary<Int, CallbackFunc> = [:]
    private var _replyTimer: Dictionary<Int, NSTimer> = [:]
    private var timeout : NSTimeInterval = 10
    private var _cookie = 1
    
    private override init() {
        super.init()
    }
    
    private func onWSOpen(){
     //   Log.info(VPaiCtrlWebSocket.TAG, msg: "onWSOpen()")
        connected = true
        dispatch_async(dispatch_get_main_queue(), {
            for e in self._listenEvents {
                e.onWebSocketOpen()
            }
        })
    }
    
    private func onWSEnd(code : Int, reason : String, wasClean : Bool, error : ErrorType?){
       // Log.info(VPaiCtrlWebSocket.TAG, msg: "onWSEnd: code=\(code), reason=\(reason)")
        connected = false
        closeWebSocket()
        dispatch_async(dispatch_get_main_queue(), {
            for e in self._listenEvents {
                e.onWebSocketClose()
            }
        })
    }
    
    private func onWSMessage(data : Any){
        if let text = data as? String {
         //   Log.info(VPaiCtrlWebSocket.TAG, msg: "text = \(text)")
            let json = SwiftyJSON.JSON.parse(text)
            let cookie = json["cookie"].int ?? -1
            if let cb = _replyMap[cookie] {
                _replyTimer[cookie]?.invalidate()
                _replyTimer[cookie] = nil
                _replyMap[cookie] = nil
                let args = CallbackArgs()
                args.ret = json["ret"].int ?? 0
                args.json = json
                dispatch_async(dispatch_get_main_queue(), {
                    cb(args: args)
                })
                return
            }
            
            let action = json["action"].string ?? "unknow"
            switch action {
            case ACTION_GET_DEVICE_INFO:
                handleDeviceInfo(json)
            case ACTION_UPDATE_DEVICE_RESPONSE:
                handleUpdatedeviceResponse(json)
            case ACTION_NOTIFY_WIFI_STATUS:
                handleNotifyWifiStatus(json)
            default: break
              //  Log.error(VPaiCtrlWebSocket.TAG, msg: "unknow action:\(action)")
            }
        }
    }
    
    private func handleDeviceInfo(json: SwiftyJSON.JSON){
        let info = VPaiDeviceInfo.parseFromJson(json)
        dispatch_async(dispatch_get_main_queue(), {
            for e in self._listenEvents {
                e.onDeviceInfo(info: info)
            }
        })
    }
    
    private func handleUpdatedeviceResponse(json: SwiftyJSON.JSON){
        let persent = json["finished"].string ?? "0"
        dispatch_async(dispatch_get_main_queue(), {
            for e in self._listenEvents {
                e.onUpdatedeviceResponse(percent: Int(persent) ?? 0)
            }
        })
    }
    
    private func handleNotifyWifiStatus(json: SwiftyJSON.JSON) {
        let status = WifiStatus.paresFromJson(json)
        dispatch_async(dispatch_get_main_queue(), {
            for e in self._listenEvents {
                e.onNotifyWifiStatus(status: status)
            }
        })
    }
    
    private func getRandomNumberString(count: Int) -> String {
        var result = ""
        while(true) {
            let random = arc4random()
            result += "\(random)"
            if result.characters.count > count {
                break
            }
        }
        return (result as NSString).substringToIndex(count)
    }
}

class CallbackArgs {
    var ret:Int = -1
    var flag:Int = 0 // 判断有无sdcard的标志
    var json:SwiftyJSON.JSON = []
    var binary:[UInt8]?
}
typealias CallbackFunc = (args : CallbackArgs)->()

class VPaiCtrlWebSocketEvents: NSObject{
    
    var onWebSocketOpen : () -> () = {}
    var onWebSocketClose : () -> () = {}
    var onDeviceInfo : (info: VPaiDeviceInfo) -> () = {info in}
    var onUpdatedeviceResponse : (percent: Int) -> () = {percent in}
    var onNotifyWifiStatus : (status: WifiStatus) -> () = {status in}

    //used to compare object when call unregisterEvent
    private var id : Int = 0
}

class VPaiDeviceInfo {
    var version = 0
    var product = ""
    var manufactory = ""
    var module = ""
    var sdsize = 0
    var sdused = 0
    var remainbattery = 0
    var devicestatus = 0
    var externalpower = 0
    var mac = ""
    var deviceid = ""
    var stationenabled = 0
    
    class func parseFromJson(json: SwiftyJSON.JSON) -> VPaiDeviceInfo{
        let info = VPaiDeviceInfo()
        info.version = json["version"].int ?? 0
        info.product = json["product"].string ?? ""
        info.manufactory = json["manufactory"].string ?? ""
        info.module = json["module"].string ?? ""
        info.sdsize = json["sdsize"].int ?? 0
        info.sdused = json["sdused"].int ?? 0
        info.remainbattery = json["remainbattery"].int ?? 0
        info.devicestatus = json["devicestatus"].int ?? 0
        info.externalpower = json["externalpower"].int ?? 0
        info.mac = json["mac"].string ?? ""
        info.deviceid = json["deviceid"].string ?? ""
        info.stationenabled = json["stationenabled"].int ?? 0
        return info
    }
}

class NetConfig {
    var wifimode = ""
    var staip = ""
    var softapssid = ""
    var softappasswd = ""
    var stassid = ""
    var stapasswd = ""
    
    class func parseFromJson(json: SwiftyJSON.JSON) -> NetConfig {
        let config = NetConfig()
        config.wifimode = json["wifimode"].string ?? ""
        config.staip = json["staip"].string ?? ""
        config.softapssid = json["softapssid"].string ?? ""
        config.softappasswd = json["softappasswd"].string ?? ""
        config.stassid = json["stassid"].string ?? ""
        config.stapasswd = json["stapasswd"].string ?? ""
        return config
    }
}


class DateTimeInfo {
    var date = "" //格式为：xxxx/xx/xx
    var time = "" //格式为：xx:xx:xx
    var tiemzone = ""
    
    init(){
        let today = NSDate()
        let dateFormatter = NSDateFormatter()
        dateFormatter.dateFormat = "yyyy/MM/dd"
        date = dateFormatter.stringFromDate(today)
        dateFormatter.dateFormat = "HH:mm:ss"
        time = dateFormatter.stringFromDate(today)
        let systemZone = NSTimeZone.localTimeZone()
        tiemzone = systemZone.abbreviation!
    }
    
    class func parseFromJson(json: SwiftyJSON.JSON) -> DateTimeInfo{
        let dateAndTime = DateTimeInfo()
        dateAndTime.date = json["date"].string ?? ""
        dateAndTime.time = json["time"].string ?? ""
        dateAndTime.tiemzone = json["timezone"].string ?? ""
        return dateAndTime
    }
}

class WifiInfo {
    var ssid = ""
    var security = ""
    var signal = 0
    
    init() {
        
    }
    
    init(ssid: String, security: String, signal: Int){
        self.ssid = ssid
        self.security = security
        self.signal = signal
    }
    
    class func paresFromJson(json: SwiftyJSON.JSON) -> WifiInfo{
        let info = WifiInfo()
        info.ssid = json["ssid"].string ?? ""
        info.security = json["security"].string ?? ""
        info.signal = json["signal"].int ?? 0
        return info
    }
}

//当type等于NETWORK_STATE_CHANGED_ACTION时，value为1代表Station网络连接，value为0代表Station网络断开。 
//当type等于其他值时，value均为0.
class WifiStatus {
    var type = WifiActionEvent.STATE_NONE
    var value = 0
    
    class func paresFromJson(json: SwiftyJSON.JSON) -> WifiStatus{
        let status = WifiStatus()
        let type = json["type"].int ?? 0
        status.type = WifiActionEvent(rawValue: type) ?? WifiActionEvent.STATE_NONE
        status.value = json["value"].int ?? 0
        return status
    }
}

enum LanguageType: String {
    case EN = "en", ZH = "zh", SC = "sc", TC = "tc"//分别对应英语 中文 简体中文  繁体中文
}

enum WifiActionEvent: Int {
    case STATE_NONE = 0,
    SCAN_RESULTS_AVAILABLE_ACTION,
    NETWORK_STATE_CHANGED_ACTION,    // 网络状态改变
    DHCP_SUCCESS_ACTION,
    AUTHENTICATION_FAILURE_ACTION,    // 密码错误
    STATION_STARTED_ACTION,
    STATION_STOPPED_ACTION,
    AP_STARTED_ACTION,
    AP_STOPPED_ACTION,
    AP_STA_CONNECTED_ACTION,
    AP_STA_DISCONNECTED_ACTION,
    SUPPLICANT_STATE_CHANGE_ACTION,
    ASSOC_REJECT_ACTION,
    WPS_SUCCESS_EVENT,
    WPS_FAIL_EVENT,
    WPS_OVERLAP_EVENT,
    WPS_TIMEOUT_EVENT,
    WPS_AP_AVAILABLE_PBC_EVENT
}